#ifndef __M_EXCHANGER_H__
#define __M_EXCHANGER_H__
#include"../mqcommon/helper.hpp"
#include"../mqcommon/logger.hpp"
#include"../mqcommon/mq_msg.pb.h"
#include<google/protobuf/map.h>
#include<unordered_map>
#include<memory>
#include<mutex>
#include<sstream>

namespace mq{
    struct Exchange{
        using ptr = std::shared_ptr<Exchange>;

        std::string name;
        ExchangeType type;
        bool durable;
        bool auto_delete;
        google::protobuf::Map<std::string,std::string> args;
        Exchange(){}
        Exchange(const std::string& iname,ExchangeType itype,bool idur,bool idele,
        const google::protobuf::Map<std::string,std::string>& iargs)
        :name(iname),type(itype),durable(idur),auto_delete(idele),args(iargs){}
        //解析序列化//k1=v1&
        void setargs(const std::string& str_args)
        {
            const std::string sep1 = "=";
            const std::string sep2 = "&";
            std::vector<std::string> vcs;
            Stringsplit::split(str_args,&vcs,sep2);
            for(auto& str:vcs){
                size_t pos = str.find(sep1);
                std::string key = str.substr(0,pos);
                std::string val = str.substr(pos+sep1.size());
                args.insert({key,val});
            }
            //myservion not test
            // size_t pos,start = 0;
            // while(start < str_args.size()){
            //     pos = str_args.find("=",start);
            //     if(pos == std::string::npos){
            //         break;
            //     }
            //     std::string key = str_args.substr(start,pos);
            //     start = pos + sep1.size();

            //     pos = str_args.find("&",start);
            //     std::string val = str_args.substr(start,pos);
            //     args.insert(std::make_pair(key,val));
            //     start = pos + sep2.size();
            // } 
        }
        //序列化传输
        std::string getargsstr(){
            std::string str_args;
            if(!args.empty()){
                for(auto e:args){
                    str_args += e.first + "=" + e.second + "&";
                }
            }
            return str_args;
        }
        
    };

    using ExchangeMap = std::unordered_map<std::string,Exchange::ptr>;
    class ExchangeSqlMaper{
        public:

            ExchangeSqlMaper(const std::string& db_file):_sql_hander(db_file){
                std::string path = Filehelper::parentDirectory(db_file);
                Filehelper::createDirectory(path);
                assert(_sql_hander.open());
                createTable();
            }
            void createTable(){
                //create table if not exists exchange_table(
                //name varchar(32) primary key,
                //type int,
                //durable int, 
                //auto_delect int,
                //args varchar(64));
                std::stringstream ss;
                ss<<"create table if not exists exchange_table(";
                ss<<"name varchar(32) primary key,";
                ss<<"type int, durable int,";
                ss<<"auto_delect int, args varchar(64));";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("create exchange_table false");
                    abort();
                }
            }
            void deleteTable(){
                //drop table if exists exchange_table
                std::stringstream ss;
                ss<<"drop table if exists exchange_table;";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("drop exchange_table false");
                    abort();
                }
            }
            void insert(Exchange::ptr& exchange){
                //name varchar(32) primary key,
                //type int,
                //durable int, 
                //auto_delect int,
                //args varchar(32));
                //insert into exchange_table values('%s',%d,%d,%d, '%s')
                std::string args = exchange->getargsstr();
                std::stringstream ss;
                ss<<"insert into exchange_table values(";
                ss<<"'"<<exchange->name<<"',";
                ss<<exchange->type<<",";
                ss<<exchange->durable<<",";
                ss<<exchange->auto_delete<<",";
                ss<<"'"<<args.c_str()<<"');";
                _sql_hander.exec(ss.str(),nullptr,nullptr);

            }
            void remove(const std::string& name){
                //delect  from exchange_table where name = name
                std::stringstream ss;
                ss<<"delete from exchange_table where name = '"<< name.c_str() <<"';";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr); 
            }
            
            ExchangeMap recovery(){
                ExchangeMap result;
                std::stringstream ss;
                //sqlit3没加分号，搞半天
                ss<<"select name, type, durable, auto_delect, args from exchange_table;";
                int ret = _sql_hander.exec(ss.str(),ExchangeCallback,&result);
                if(ret == false) {
                    LOG_ERRORM("recovery exchange false...");
                }
                return result;
            }
        private:
            //int(*SqliteCallback)(void*,int,char**,char**);
            static int ExchangeCallback(void* arg,int col,char** row,char** field_name){
                ExchangeMap* echmap = (ExchangeMap*)arg;
                auto sechange = std::make_shared<Exchange>();
                sechange->name = row[0];
                sechange->type = (mq::ExchangeType)std::stoi(row[1]);
                sechange->durable = std::stoi(row[2]);
                sechange->auto_delete = std::stoi(row[3]);
                if(row[4]) sechange->setargs(row[4]);
                echmap->insert(std::make_pair(sechange->name,sechange));
                return 0;
            }
            
        private:
            SqliteHelper _sql_hander;
    };

    class ExchangeManager{
        public:
        using ptr = std::shared_ptr<ExchangeManager>;
            ExchangeManager(const std::string& db_file):_Mapersql(db_file){
                _exchanges = _Mapersql.recovery();
                LOG_DEBUGM("exchanger init...");

            }

            bool declare(const std::string& iname,ExchangeType itype,bool idur,bool idele,const google::protobuf::Map<std::string,std::string>& iargs){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _exchanges.find(iname);
                if(it != _exchanges.end()) 
                    return true;

                Exchange excha(iname, itype, idur, idele,iargs);
                Exchange::ptr exchaptr = std::make_shared<Exchange>(excha);
                _exchanges.insert(std::make_pair(iname,exchaptr));
                    if(idur == true) _Mapersql.insert(exchaptr);
                return true;
            }
                
            void deleteExchange(const std::string& name){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _exchanges.find(name);
                if(it == _exchanges.end()){
                    return;
                }
                if(it->second->durable) _Mapersql.remove(name);
                _exchanges.erase(name);

            }

            bool exists(const std::string& name){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _exchanges.find(name);
                if(it == _exchanges.end()){
                    return false;
                }
                return true;
            }
            Exchange::ptr select(const std::string& name){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _exchanges.find(name);
                if(it == _exchanges.end()){
                    return nullptr;
                }
                return it->second;
            }

            size_t size(){
                std::unique_lock<std::mutex> lock(_mutex);
                return _exchanges.size();
            }
        
            void clear(){
                std::unique_lock<std::mutex> lock(_mutex);
                _exchanges.clear();
                _Mapersql.deleteTable();
            }

        private:
        std::mutex _mutex;
        ExchangeSqlMaper _Mapersql;
        std::unordered_map<std::string,Exchange::ptr> _exchanges;
    };

}
#endif